﻿using System;
using System.Collections.Specialized;
using System.Configuration;
using System.Deployment.Application;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Security.Policy;
using System.Text.RegularExpressions;

namespace NAGFwk.UI
{
       /// <summary>
    /// Representa la lógica de acceso a los datos de configuracion utilizada por ExceptionEmailHelper
    /// </summary>
    public static class AppSettingsFromAssembly
    {
        private static string _strAppBase;
        private static string _strConfigPath;
        private static string _strSecurityZone;
        private static string _strRuntimeVersion;
        private static NameValueCollection _objCommandLineArgs;
        private static NameValueCollection _objAssemblyAttribs;

           #region "Properties"

        /// <summary>
        /// Obtiene si se esta ejecutando en modo Debug
        /// </summary>
        public static bool DebugMode
        {
            get
            {
                if (CommandLineArgs["debug"] == null)
                {
                    return Debugger.IsAttached;
                }
                else
                {
                    return true;
                }
            }
        }

        /// <summary>
        /// Fecha de compilación
        /// </summary>
        public static string AppBuildDate
        {
            get
            {
                if (_objAssemblyAttribs == null)
                {
                    _objAssemblyAttribs = GetAssemblyAttribs();
                }
                return _objAssemblyAttribs["BuildDate"];
            }
        }

        /// <summary>
        /// Nombre del producto
        /// </summary>
        public static string AppProduct
        {
            get
            {
                if (_objAssemblyAttribs == null)
                {
                    _objAssemblyAttribs = GetAssemblyAttribs();
                }
                return _objAssemblyAttribs["Product"];
            }
        }

        /// <summary>
        /// Nombre de la compañia
        /// </summary>
        public static string AppCompany
        {
            get
            {
                if (_objAssemblyAttribs == null)
                {
                    _objAssemblyAttribs = GetAssemblyAttribs();
                }
                return _objAssemblyAttribs["Company"];
            }
        }

        /// <summary>
        /// Copyright
        /// </summary>
        public static string AppCopyright
        {
            get
            {
                if (_objAssemblyAttribs == null)
                {
                    _objAssemblyAttribs = GetAssemblyAttribs();
                }
                return _objAssemblyAttribs["Copyright"];
            }
        }

        /// <summary>
        /// Descripcion de la aplicacion
        /// </summary>
        public static string AppDescription
        {
            get
            {
                if (_objAssemblyAttribs == null)
                {
                    _objAssemblyAttribs = GetAssemblyAttribs();
                }
                return _objAssemblyAttribs["Description"];
            }
        }

        /// <summary>
        /// Titulo de la aplicacion
        /// </summary>
        public static string AppTitle
        {
            get
            {
                if (_objAssemblyAttribs == null)
                {
                    _objAssemblyAttribs = GetAssemblyAttribs();
                }
                return _objAssemblyAttribs["Title"];
            }
        }

        /// <summary>
        /// Nombre del fichero (.exe, .dll)
        /// </summary>
        public static string AppFileName
        {
            get { return Regex.Match(AppPath, "[^/]*.(exe|dll)", RegexOptions.IgnoreCase).ToString(); }
        }

        /// <summary>
        /// Obtiene el path de la aplicacion
        /// </summary>
        public static string AppPath
        {
            get
            {
                if (_objAssemblyAttribs == null)
                {
                    _objAssemblyAttribs = GetAssemblyAttribs();
                }
                return _objAssemblyAttribs["CodeBase"];
            }
        }

        /// <summary>
        /// Nombre completo de la aplicacion
        /// </summary>
        public static string AppFullName
        {
            get
            {
                if (_objAssemblyAttribs == null)
                {
                    _objAssemblyAttribs = GetAssemblyAttribs();
                }
                return _objAssemblyAttribs["FullName"];
            }
        }

        /// <summary>
        /// Obtiene una coleccion nombre/valor de los argumentos utilizados al iniciar la aplicación
        /// </summary>
        public static NameValueCollection CommandLineArgs
        {
            get { return _objCommandLineArgs ?? (_objCommandLineArgs = GetCommandLineArgs()); }
        }

        /// <summary>
        /// Version de Runtime
        /// </summary>
        public static string RuntimeVersion
        {
            get {
                return _strRuntimeVersion ??
                       (_strRuntimeVersion = Regex.Match(Environment.Version.ToString(), "\\d+.\\d+.\\d+").ToString());
            }
        }

        /// <summary>
        /// Version de la aplicacion
        /// </summary>
        public static string AppVersion
        {
            get
            {
                if (ApplicationDeployment.IsNetworkDeployed)
                {
                    return ApplicationDeployment.CurrentDeployment.CurrentVersion.ToString();
                }


                if (_objAssemblyAttribs == null)
                {
                    _objAssemblyAttribs = GetAssemblyAttribs();
                }
                return _objAssemblyAttribs["Version"];
            }
        }

        /// <summary>
        /// Path del archivo de configuración
        /// </summary>
        public static string ConfigPath
        {
            get
            {
                return _strConfigPath ?? (_strConfigPath = Convert.ToString(
                    AppDomain.CurrentDomain.GetData("APP_CONFIG_FILE"),
                    new CultureInfo("")));
            }
        }

        /// <summary>
        /// AppBase
        /// </summary>
        public static string AppBase
        {
            get
            {
                if (_strAppBase == null)
                {
                    _strAppBase = Convert.ToString(
                        AppDomain.CurrentDomain.GetData("APPBASE"), new CultureInfo(""));
                }
                return _strAppBase;
            }
        }

        /// <summary>
        /// SecurityZone
        /// </summary>
        public static string SecurityZone
        {
            get { return _strSecurityZone ?? (_strSecurityZone = Zone.CreateFromUrl(AppBase).SecurityZone.ToString()); }
        }

        #endregion

        /// <summary>
        /// Assembly
        /// </summary>
        /// <returns>Assembly</returns>
        private static Assembly GetEntryAssembly()
        {
            return Assembly.GetEntryAssembly() ?? Assembly.GetCallingAssembly();
        }


           /// <summary>
        /// returns string name / string value pair of all attribs
        /// for specified assembly
        /// note that Assembly* values are pulled from AssemblyInfo file in project folder
        /// </summary>
        /// <returns>Product = AssemblyProduct string
        /// Copyright = AssemblyCopyright string
        /// Company = AssemblyCompany string
        /// Description = AssemblyDescription string
        /// Title = AssemblyTitle string</returns>
        private static NameValueCollection GetAssemblyAttribs()
        {
            string strAttribName;
            NameValueCollection objNameValueCollection = new NameValueCollection();
            Assembly objAssembly = GetEntryAssembly();

            var objAttributes = objAssembly.GetCustomAttributes(false);
            foreach (object objAttribute in objAttributes)
            {
                strAttribName = objAttribute.GetType().ToString();
                var strAttribValue = "";
                switch (strAttribName)
                {
                    case "System.Reflection.AssemblyTrademarkAttribute":
                        strAttribName = "Trademark";
                        strAttribValue = ((AssemblyTrademarkAttribute)objAttribute).Trademark;
                        break;
                    case "System.Reflection.AssemblyProductAttribute":
                        strAttribName = "Product";
                        strAttribValue = ((AssemblyProductAttribute)objAttribute).Product;
                        break;
                    case "System.Reflection.AssemblyCopyrightAttribute":
                        strAttribName = "Copyright";
                        strAttribValue = ((AssemblyCopyrightAttribute)objAttribute).Copyright;
                        break;
                    case "System.Reflection.AssemblyCompanyAttribute":
                        strAttribName = "Company";
                        strAttribValue = ((AssemblyCompanyAttribute)objAttribute).Company;
                        break;
                    case "System.Reflection.AssemblyTitleAttribute":
                        strAttribName = "Title";
                        strAttribValue = ((AssemblyTitleAttribute)objAttribute).Title;
                        break;
                    case "System.Reflection.AssemblyDescriptionAttribute":
                        strAttribName = "Description";
                        strAttribValue = ((AssemblyDescriptionAttribute)objAttribute).Description;
                        break;
                    default:
                        break;
                    //Console.WriteLine(strAttribName)
                }
                if (!string.IsNullOrEmpty(strAttribValue))
                {
                    objNameValueCollection.Add(strAttribName, strAttribValue);
                    /*if (objNameValueCollection[strAttribName] == "")
                    {
                        objNameValueCollection.Add(strAttribName, strAttribValue);
                    }*/
                }
            }

            //-- add some extra values that are not in the AssemblyInfo, but nice to have
            {
                objNameValueCollection.Add("CodeBase", objAssembly.CodeBase.Replace("file:///", ""));
                objNameValueCollection.Add("BuildDate", AssemblyBuildDate(objAssembly, false).ToString());
                objNameValueCollection.Add("Version", objAssembly.GetName().Version.ToString());
                objNameValueCollection.Add("FullName", objAssembly.FullName);
            }

            //-- we must have certain assembly keys to proceed.
            if (objNameValueCollection["Product"] == null)
            {
                throw new MissingFieldException("The AssemblyInfo file for the assembly " + objAssembly.GetName().Name + " must have the <Assembly:AssemblyProduct()> key populated.");
            }
            if (objNameValueCollection["Company"] == null)
            {
                throw new MissingFieldException("The AssemblyInfo file for the assembly " + objAssembly.GetName().Name + " must have the <Assembly: AssemblyCompany()> key populated.");
            }

            return objNameValueCollection;
        }



        /// <summary>
        /// when this app is loaded via URL, it is possible to pass in "command line arguments" like so:
        ///
        /// string[] args = {
        /// "C:\WINDOWS\Microsoft.NET\Framework\v1.0.3705\IEExec",
        /// "3", "1", "86474707A3C6F63616C686F6374710000000"};
        /// </summary>
        /// <param name="strUrl"></param>
        /// <param name="objNameValueCollection"></param>
        private static void GetUrlCommandLineArgs(string strUrl, ref NameValueCollection objNameValueCollection)
        {

            MatchCollection objMatchCollection;

            //-- http://localhost/App.Website/App.Loader.exe?a=1&b=2&c=apple
            //-- a = 1
            //-- b = 2
            //-- c = apple
            objMatchCollection = Regex.Matches(strUrl, "(?<Key>[^=#&?]+)=(?<Value>[^=#&]*)");
            foreach (Match objMatch in objMatchCollection)
            {
                objNameValueCollection.Add(objMatch.Groups["Key"].ToString(), objMatch.Groups["Value"].ToString());
            }
        }

        /// <summary>
        /// Devuelve verdadero si la url es valida
        /// </summary>
        /// <param name="strAny">url a evaluar</param>
        /// <returns></returns>
        private static bool IsUrl(string strAny)
        {
            return strAny.IndexOf("&", StringComparison.Ordinal) > -1 ||
                strAny.StartsWith("?", StringComparison.Ordinal) ||
                strAny.StartsWith("http://", StringComparison.OrdinalIgnoreCase);
        }

        private static string RemoveArgPrefix(string strArg)
        {
            if (strArg.StartsWith("-", StringComparison.Ordinal) |
                strArg.StartsWith("/", StringComparison.Ordinal))
            {
                return strArg.Substring(1);
            }
            else
            {
                return strArg;
            }
        }

        //--
        //-- breaks space delimited command line arguments into key value pairs, if they exist
        //--
        //-- App.Loader.exe -remoting=0 /sample=yes c=true
        //-- remoting = 0
        //-- sample = yes
        //-- c = true
        //--
        private static bool GetKeyValueCommandLineArg(string strArg, ref NameValueCollection objNameValueCollection)
        {

            MatchCollection objMatchCollection;

            objMatchCollection = Regex.Matches(strArg, "(?<Key>^[^=]+)=(?<Value>[^= ]*$)");
            if (objMatchCollection.Count == 0)
            {
                return false;
            }
            else
            {
                foreach (Match objMatch in objMatchCollection)
                {
                    objNameValueCollection.Add(RemoveArgPrefix(objMatch.Groups["Key"].ToString()), objMatch.Groups["Value"].ToString());
                }
                return true;
            }
        }

        //--
        //-- parses command line arguments, handling special case when app was launched via URL
        //-- note that the default .GetCommandLineArgs is SPACE DELIMITED !
        //--
        private static NameValueCollection GetCommandLineArgs()
        {
            string[] strArgs = Environment.GetCommandLineArgs();
            NameValueCollection objNameValueCollection = new NameValueCollection();

            if (strArgs.Length > 0)
            {
                //--
                //-- handles typical case where app was launched via local .EXE
                //--
                int intArg = 0;
                foreach (string strArg in strArgs)
                {
                    if (IsUrl(strArg))
                    {
                        GetUrlCommandLineArgs(strArg, ref objNameValueCollection);
                    }
                    else
                    {
                        if (!GetKeyValueCommandLineArg(strArg, ref objNameValueCollection))
                        {
                            objNameValueCollection.Add("arg" + intArg, RemoveArgPrefix(strArg));
                            intArg += 1;
                        }
                    }
                }
            }

            return objNameValueCollection;
        }

        //--
        //-- exception-safe file attrib retrieval; we don't care if this fails
        //--
        private static DateTime AssemblyFileTime(Assembly objAssembly)
        {
            try
            {
                return File.GetLastWriteTime(objAssembly.Location);
            }
            catch (UnauthorizedAccessException)
            {
                return DateTime.MaxValue;
            }
            catch (ArgumentException)
            {
                return DateTime.MaxValue;
            }
            catch (PathTooLongException)
            {
                return DateTime.MaxValue;
            }
            catch (NotSupportedException)
            {
                return DateTime.MaxValue;
            }

        }

        //--
        //-- returns build datetime of assembly
        //-- assumes default assembly value in AssemblyInfo:
        //-- <Assembly: AssemblyVersion("1.0.*")>
        //--
        //-- filesystem create time is used, if revision and build were overridden by user
        //--
        private static DateTime AssemblyBuildDate(Assembly objAssembly, bool blnForceFileDate)
        {
            Version objVersion = objAssembly.GetName().Version;
            DateTime dtBuild;

            if (blnForceFileDate)
            {
                dtBuild = AssemblyFileTime(objAssembly);
            }
            else
            {
                dtBuild = DateTime.ParseExact("01/01/2000", "dd/MM/yyyy", CultureInfo.InvariantCulture).AddDays(objVersion.Build).AddSeconds(objVersion.Revision * 2);
                if (TimeZone.IsDaylightSavingTime(dtBuild, TimeZone.CurrentTimeZone.GetDaylightChanges(dtBuild.Year)))
                {
                    dtBuild = dtBuild.AddHours(1);
                }
                if (dtBuild > DateTime.Now | objVersion.Build < 730 | objVersion.Revision == 0)
                {
                    dtBuild = AssemblyFileTime(objAssembly);
                }
            }

            return dtBuild;
        }

        /// <summary>
        /// Returns the specified application value as a boolean
        /// </summary>
        /// <param name="key">Llave de configuración</param>
        /// <returns>
        /// True values: 1, True, true
        /// False values: anything else
        ///</returns>
        public static bool GetBoolean(string key)
        {
            string strTemp = ConfigurationManager.AppSettings.Get(key);
            if (strTemp == null)
            {
                return false;
            }
            else
            {
                switch (strTemp.ToUpperInvariant())
                {
                    case "1":
                    case "TRUE":
                        return true;
                    default:
                        return false;
                }
            }
        }

        //-- 
        /// <summary>
        /// Returns the specified String value from the application .config file
        /// </summary>
        /// <param name="key">Llave de configuración</param>
        /// <returns>Valor de configuración</returns>
        public static string GetString(string key)
        {
            string strTemp = (string)ConfigurationManager.AppSettings.Get(key);
            if (string.IsNullOrEmpty(strTemp))
            {
                return "";
            }
            else
            {
                return strTemp;
            }
        }


        /// <summary>
        /// Returns the specified Integer value from the application .config file
        /// </summary>
        /// <param name="key">Llave de configuración</param>
        /// <returns>Valor de configuración</returns>
        [SuppressMessage("Microsoft.Naming", "CA1720:IdentifiersShouldNotContainTypeNames", MessageId = "integer")]
        public static int GetInteger(string key)
        {
            string value = ConfigurationManager.AppSettings.Get(key);
            if (value == null)
            {
                return 0;
            }
            else
            {
                return int.Parse(value, CultureInfo.InvariantCulture);
            }
        }

    }
}
